home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
scope
/
001-025
/
scopedisk8
/
qint
/
qint.c
next >
Wrap
C/C++ Source or Header
|
1995-03-18
|
8KB
|
284 lines
/*
* QINT.C
*
* Prioritized Interrupt scheme based on exceptions (and therefore
* standard signals).
*
* NOTE the following bugs in EXEC exception handling:
* -If you ever use Forbid()/Permit() pair, An exception which comes
* in within the pair will not get processed after the Permit()
* until one of several system calls is made to force a re-evaluation.
*
* -We cannot simply return a mask at the end of the exception
* handler because if a signal comes in after the exception occurs
* but before we return, the same problem as with Forbid()/Permit()
* comes up.
*
* Calling SetExcept() while NOT Forbid()n appears to fix the problem.
*/
#include <exec/types.h> /* Includes. You might have to add some */
#include <exec/nodes.h> /* If I missed any. */
#include <exec/lists.h>
#include <exec/tasks.h>
typedef unsigned char ubyte;
typedef unsigned short uword;
typedef unsigned long ulong;
typedef struct Task TASK;
typedef struct Node NODE;
typedef struct List LIST;
typedef void (*FPTR)();
extern TASK *FindTask();
#define QINT struct _QINT
QINT { /* 32 bytes total */
NODE Node; /* 14 bytes */
FPTR vector; /* Function to call */
long sigmask; /* signal mask (1 bit set) */
long arg; /* argument to handler */
ubyte filler2[6];
};
/*
* Note: Since -128 is the lowest priority possible, setting an
* exception's priority to -128 means it will never occur.
*/
static char QPri = -128; /* Current process Q interrupt priority */
static QINT QInt[32]; /* Q interrupts which are really exceptions */
static LIST QList; /* List of pending Q interrupts */
static APTR QSaveExcept;
static short QInts;
static short QInHan; /* Currently in the exception queue handler */
void except();
void QInit();
#asm
; _EXCEPT
;
; Exception handler.
;
; ALL data and address registers are saved by EXEC. D0
; holds the exceptions that occured on entry, and A1
; holds the exception data frame (which we do not use,
; but need to save in case there are exceptions that
; we do not own).
public _LVOForbid
public _LVOPermit
public _LVOEnqueue
public _LVOSetSignal
public _geta4 ;Aztec: Load proper address reg.
public _QSaveExcept
public _QInHan
_except:
move.l A1,-(sp) ;save except data segment
move.l 4,A6 ;A6 = Exec Base
move.l D0,D6 ;D6 = Exception Bit Mask
jsr _geta4 ;get global base register (Aztec small code)
;;sub.w #1,_QLevel ;level down!
;;bpl .ex0
;; ; STACKING LIMIT REACHED
;;add.w #1,_QLevel ;down too far!
;;add.l #1,_QError ;mark it
;;jsr _LVOForbid(A6) ;Reload all exceptions that occured
;;move.l D6,D0
;;move.l D6,D1
;;jsr _LVOSetSignal(A6)
;;jsr _LVOPermit(A6)
;;move.l D6,D0 ;Reenable all exceptions that
;; ; occured by returning the mask in D0
;;addq.l #4,sp ;restore stack frame
;;rts
; Queue any exceptions which are interrupts. Exceptions
; for interrupts which are queued are NOT reenabled until
; they are actually run.
;
; Note that in the loop we must loop to .ex2 to decrement
; D4, which doesn't occur when we find a '1'. The Z bit
; must be set when we loop to .ex2
.ex0 moveq.l #31,D4 ;D4 = BIT NUMBER
.ex1 btst.l D4,D6 ; test bits
.ex2 dbne D4,.ex1 ; until found a '1'
beq .ex10 ;or loop exhausted (D4 == -1)
move.l D4,D5 ;Calculate address of QINT.
asl.l #5,D5 ;D5 = index * sizeof(QINT)
add.l #_QInt,D5 ; + Address
move.l D5,A3 ;A3 = QINT address
tst.l 14(A3) ;Is this exception vectored?
beq .ex2 ;no, somebody else owns it
.ex3 bclr.l D4,D6 ;clear exception bit.
jsr _LVOForbid(A6) ;Important operation!
move.l A3,A1 ;A1 = node
lea.l _QList,A0 ;A0 = List base
move.b #5,8(A1) ;mark as being queued
jsr _LVOEnqueue(A6)
jsr _LVOPermit(A6) ;enable exceptions
clr.w D7 ;Force Z cc set.
bra .ex2
; Call the handler. NOTE that handler() need only
; save/restore D6. Both Lattice and Aztec will do this.
.ex10 tst.w _QInHan ;no need to call handler?
bne .ex11
bsr _handler ;call handler
.ex11 move.l D6,D0 ;D0 = exception mask
beq .ex12 ;we processed all exceptions
move.l _QSaveExcept,A0 ;somebody else owns some exceptions
move.l (sp)+,A1 ;restore exception data pointer
;;add.w #1,_QLevel
jmp (A0) ;call him with remaining exceptions.
.ex12 addq.l #4,sp ;from push at top
;;add.w #1,_QLevel
rts
#endasm
/*
* Exception Queue handler!
*
* Note: The places I set QInHan may appear to be strange, but keep
* in mind that exceptions will not occur while we are Forbid()n.
*/
static
void
handler()
{
register QINT *qint;
register char savepri;
QInHan = 1;
Forbid();
while ((qint = (QINT *)QList.lh_Head) != (QINT *)&QList.lh_Tail) {
if (qint->Node.ln_Pri <= QPri) /* priority not high enough */
break;
savepri = QPri; /* save old priority */
Remove(qint); /* remove from queue */
qint->Node.ln_Type = 0; /* mark as such */
QPri = qint->Node.ln_Pri; /* up the priority */
Permit();
(*qint->vector)(qint->arg); /* call handler */
QPri = savepri; /* restore priority */
if (qint->vector) /* reenable if still exists */
SetExcept(qint->sigmask, qint->sigmask);
Forbid();
}
QInHan = 0;
Permit();
/*
* Exceptions are not checked on return, so we must call SetExcept()
* here to 'force' a check (i.e. the exception signal comes in before
* the exception handler returns).
*/
SetExcept(0,0);
}
static
void
QInit()
{
NewList(&QList);
}
/*
* ----------------------------------------------------------
*
* SETQVECTOR()
*
* Vector a signal at a specified priority. Setting the vector to NULL
* Removes the Q Interrupt. Exceptions are automatically enabled or
* disabled for the signal bit.
*/
FPTR
SetQVector(signo, vector, arg, pri)
FPTR vector;
long arg;
long signo;
char pri;
{
register QINT *qint = QInt + signo;
register long sigmask = 1 << signo;
register FPTR oldvector;
Forbid();
if (!QList.lh_Head) /* Init (first call) */
QInit();
oldvector = qint->vector;
if (!vector) { /* kill the Q int */
if (oldvector) { /* one less */
SetExcept(0, sigmask); /* disable exception */
if (qint->Node.ln_Type == 5) { /* If queued to go */
Remove(qint); /* Remove from queue */
qint->Node.ln_Type = 0;
}
if (--QInts == 0) /* No more Q Ints */
FindTask(NULL)->tc_ExceptCode = QSaveExcept;
}
} else {
if (!oldvector) { /* New Interrupt? */
if (++QInts == 1) { /* First Interrupt? */
QSaveExcept = FindTask(NULL)->tc_ExceptCode;
FindTask(NULL)->tc_ExceptCode = (APTR)except;
}
}
qint->arg = arg;
qint->Node.ln_Pri = pri; /* set priority */
qint->sigmask = sigmask; /* set signal mask */
SetExcept(sigmask, sigmask); /* enable exception */
}
qint->vector = vector; /* set vector */
Permit(); /* reenable exceptions */
SetExcept(0,0); /* force mask check */
return(oldvector);
}
/*
* SETQPRI()
*
* Set the task's Q priority
*
* Note: I *could* use a Forbid()/Permit() pair, but due to the
* exception handling bug noted above, I would then have to do a
* SetExcept(0,0).
*/
char
SetQPri(newpri)
char newpri;
{
char oldpri;
if (!QList.lh_Head) /* Init (first call) */
QInit();
Disable(); /* Modify the priority */
oldpri = QPri;
QPri = newpri;
Enable();
/*
* Don't bother calling the handler unless there is something
* queued.
*/
if (QList.lh_Head != (APTR)&QList.lh_Tail)
handler();
return(oldpri);
}